/* eslint-disable import/no-dynamic-require,no-shadow,no-cond-assign,radix */
const fs = require('fs');
const path = require('path');
const cmd = require('child_process');
const {
    basePath,
} = require('./rebasePath')();

/**
 * 返回是一个方法，该方法可以调用浏览器的 fetchEvent 方法
 *
 * 这些方法对应于 /browser/fetchEventDear/events 中定义的方法
 */
module.exports.fetchEventApi = (electron) => {
    const fetchBackListener = new (class fetchBackListener {
        constructor() {
            this.events = {
                udf() {},
            };
        }
        set(id, cb) {
            this.events[id] = cb;
        }
        run(arg) {
            const cb = this.events[arg.id || 'udf'];
            (cb || (() => {}))(arg);
            delete this.events[arg.id];
        }
        runAsync(arg) {
            const cb = this.events[arg.id || 'udf'];
            (cb || (() => {}))(arg);
            if (arg.status === 'over') {
                cb(false);
                delete this.events[arg.id];
            }
        }
    })();
    const ipc = electron.ipcRenderer;
    ipc.on('fetchBack', (event, arg) => {
        fetchBackListener.run(arg);
        // console.log('fetchBack' , arg);
    });
    ipc.on('fetchBackAsync', (event, arg) => {
        fetchBackListener.runAsync(arg);
        // console.log('fetchBack' , arg);
    });
    /**
     * action 形如 fetchEventAction 中每一个具体事件的内容
     * {
     *      action: ''
     *      param: ''
     * }
     */
    return (action, cb) => {
        const id = `id_${(new Date()).getTime()}${parseInt(Math.random() * 100000)}`;
        fetchBackListener.set(id, cb);
        ipc.send('fetchFore', Object.assign({}, action, { id }));
    };
};

module.exports.fetchEventAction = ((obj) => {
    const ret = {};
    for (const i in obj) {
        ret[i] = {};
        for (const j in obj[i]) {
            ret[i][j] = (_ => _).bind(null, obj[i][j]);
        }
    }
    return ret;
})({
    dir: {
        isEmpty: {
            event: 'dir-isEmpty',
            desc: '查看文件夹是否为空，若否，返回文件夹的文件数(根目录的文件夹数 + 文件数)',
            param: {
                path: '',
            },
        },
        getAll: {
            event: 'dir-getAll',
            desc: '获取文件夹及其子文件夹中的所有文件(排除 node_modules)',
            param: {
                path: '要遍历的目录',
                childPath: '为每个目录添加前缀（默认为 空 ）',
                isTree: '构建为树形结构',
                excludePath: '排除的目录（数组）',
            },
        },
        getFilesUnderPath: {
            event: 'dir-getFilesUnderPath',
            desc: '获取文件夹下的所有文件，不包含子文件夹',
            param: {
                path: '',
                ext: '指定扩展名（可选）（数组）',
                withInfo: '附加信息(默认为 false)',
            },
        },
        mkdir: {
            event: 'dir-mkdir',
            desc: '创建文件夹',
            param: {
                path: '',
            },
        },
        removeFileOrDir: {
            event: 'dir-removeFileOrDir',
            desc: '删除文件或文件夹',
            param: {
                path: '',
            },
        },
        copyDir: {
            event: 'dir-copyDir',
            desc: '异步复制文件夹',
            param: {
                src: '源路径',
                dist: '目标路径',
                root: '是否创建文件名和源路径一致',
            },
        },
        copyDirSync: {
            event: 'dir-copyDirSync',
            desc: '同步复制文件夹',
            param: {
                src: '源路径',
                dist: '目标路径',
                root: '是否创建文件名和源路径一致',
            },
        },
        checkFoldersExsit: {
            event: 'dir-checkFoldersExsit',
            desc: '删除文件或文件夹',
            param: {
                folders: '',
            },
        },
    },
    file: {
        append: {
            event: 'file-append',
            desc: '文件追加',
            param: {
                fileName: '文件名',
                content: '追加内容',
                binary: '该字段无效',
            },
        },
        write: {
            event: 'file-write',
            desc: '文件写入',
            param: {
                fileName: '文件名',
                content: '写入内容',
                binary: '是否为二进制(bool)',
            },
        },
        read: {
            event: 'file-read',
            desc: '文件读取',
            param: {
                fileName: '文件名',
                content: '该字段无效',
                binary: '是否为二进制(bool)',
            },
        },
        exists: {
            event: 'file-exists',
            desc: '判定文件是否存在',
            param: {
                fileName: '文件名',
                content: '该字段无效',
                binary: '该字段无效',
            },
        },
        existsWithReadOrCreate: {
            event: 'file-existsWithReadOrCreate',
            desc: '检查文件是否存在并进行读取或创建',
            param: {
                fileName: '文件名',
                content: '文件不存在时有效',
                binary: '该字段无效',
            },
        },
        copyFile: {
            event: 'file-copyFile',
            desc: '复制文件',
            param: {
                source: '源文件',
                target: '目标文件',
            },
        },
        copyFileWithReplace: {
            event: 'file-copyFileWithReplace',
            desc: '复制并替换文件',
            param: {
                source: '源文件',
                target: '目标文件',
                replaceField: '被替换的内容',
                replaceContent: '替换的内容',
            },
        },
    },
    os: {
        get: {
            event: 'os-get',
            desc: '获取系统类型',
        },
    },
    request: {
        get: {
            event: 'request-get',
            param: {
                url: '请求链接',
                headers: '头部请求',
                form: '表单数据',
            },
            desc: 'get 请求',
        },
        post: {
            event: 'request-post',
            param: {
                url: '请求链接',
                headers: '头部请求',
                form: '表单数据',
            },
            desc: 'post 请求',
        },
    },
    command: {
        copyDir: {
            event: 'command-copyDir',
            desc: '复制文件夹',
            param: {
                source: '源文件',
                target: '目标文件',
            },
        },
    },
    browserView: {
        openBrowserView: {
            event: 'browserView-openBrowserView',
            desc: '打开一个窗口',
            param: {
                width: 350,
                height: 700,
                frame: true, // 是否有边框
                modal: false, // 是否需要关闭再响应下一层窗体
                url: '', // 打开 url
                closeUrl: '', // url 跳转为 什么 时关闭
            },
        },
    },
    spawn: {
        runCommand: {
            event: 'spawn-runCommand',
            desc: '执行一个 命令',
            param: {
                command: '命令',
                args: '', // '参数，应该时一个数组'
            },

        },
    },
    mysql: {
        checkConfig: {
            event: 'mysql-checkConfig',
            desc: '检查配置信息',
            param: {},
        },
        checkConnection: {
            event: 'mysql-checkConnection',
            desc: '检查是否建立连接',
            param: {},
        },
        query: {
            event: 'mysql-query',
            desc: '执行一个sql语句',
            param: {
                sql: '一句sql',
            },
        },
        configPath: {
            event: 'mysql-configPath',
            desc: '获取 mysql 配置文件的路径用于修改',
            param: {},
        },
    },
    MDialog: {
        getFile: {
            event: 'dialog-getFile',
            desc: '打开文件对话框',
            param: {
                title: '标题',
                defaultPath: '默认路径',
                buttonLabel: '按钮',
                filters: '过滤',
            },
        },
        getDir: {
            event: 'dialog-getDir',
            desc: '打开路径对话框',
            param: {
                title: '标题',
                defaultPath: '默认路径',
                buttonLabel: '按钮',
            },
        },
        getSaveFile: {
            event: 'dialog-getSaveFile',
            desc: '打开保持文件对话框',
            param: {
                title: '标题',
                defaultPath: '默认路径',
                buttonLabel: '按钮',
                filters: '过滤',
            },
        },
        getSaveDir: {
            event: 'dialog-getSaveDir',
            desc: '打开保存路径对话框',
            param: {
                title: '标题',
                defaultPath: '默认路径',
                buttonLabel: '按钮',
            },
        },
    },
    excel: {
        writeExcel: {
            event: 'excel-writeExcel',
            desc: '打开文件对话框',
            param: {
                results: '保存内容',
                filename: '文件全路径',
                rowHeight: '行高',
            },
        },
        writeCSV: {
            event: 'excel-writeCSV',
            desc: '打开文件对话框',
            param: {
                results: '保存内容',
                filename: '文件全路径',
            },
        },
        readExcel: {
            event: 'excel-readExcel',
            desc: '打开文件对话框',
            param: {
                filename: '文件全路径',
                header: '头部（可选）',
            },
        },
        readCSV: {
            event: 'excel-readCSV',
            desc: '打开文件对话框',
            param: {
                filename: '文件全路径',
            },
        },
        dbToExcel: {
            event: 'excel-dbToExcel',
            desc: '打开文件对话框',
            param: {
                sql: '查询使用的 sql',
                filename: '文件全路径',
                keyLabel: '数据库字段对应的 label',
                sheetName: 'excel 的 sheet 名称',
            },
        },
    },
    gdal_tool: {
        gdalinfo: {
            event: 'gdal_tool-gdalinfo',
            desc: '获取遥感影像的信息',
            param: {
                path: '遥感影像的全路径',
            },
        },
    },
});

/**
 * 返回一系列文本相关的内容
 */
module.exports.initSomeElectronApi = (electron) => {
    this.dialog = ((electron) => {
        const dialog = electron.remote.dialog;
        const getFileDefaultParam = {
            title: '选择文件',
            defaultPath: null,
            buttonLabel: '确认',
            filters: [{ name: '任意文件', extensions: ['*'] }],
            properties: ['openFile'],
        };
        const getDirDefaultParam = {
            title: '选择文件夹',
            defaultPath: null,
            buttonLabel: '确定',
            properties: ['openDirectory'],
        };
        const getSaveFileDefaultParam = Object.assign({}, getFileDefaultParam, {
            showsTagField: true,
            nameFieldLabel: 'electron-save',
            title: '创建文件',
        });
        const getSaveDirDefaultParam = Object.assign({}, getDirDefaultParam, {
            showsTagField: true,
            nameFieldLabel: 'electron-save',
            securityScopedBookmarks: true,
            title: '创建文件夹',
        });
        return {
            getFile(cb, obj = { title, defaultPath, buttonLabel, filters }) {
                obj = Object.assign({}, getFileDefaultParam, obj);
                dialog.showOpenDialog(obj, cb);
            },
            getDir(cb, obj = { title, defaultPath, buttonLabel }) {
                obj = Object.assign({}, getDirDefaultParam, obj);
                dialog.showOpenDialog(obj, cb);
            },
            getSaveFile(cb, obj = { title, defaultPath, buttonLabel, filters }) {
                obj = Object.assign({}, getSaveFileDefaultParam, obj);
                dialog.showSaveDialog(obj, cb);
            },
            getSaveDir(cb, obj = { title, defaultPath, buttonLabel }) {
                obj = Object.assign({}, getSaveDirDefaultParam, obj);
                dialog.showSaveDialog(obj, cb);
            },
            makeFilter(name, extensions) {
                return {
                    name,
                    extensions,
                };
            },
        };
    })(electron);

    this.clipboard = ((electron) => {
        const clipboard = electron.clipboard;
        return {
            readImage(cb) {
                cb(clipboard.readImage());
            },
            readText(cb) {
                cb(clipboard.readText());
            },
            writeText(text) {
                clipboard.writeText(text);
            },
        };
    })(electron);

    this.shell = ((electron) => {
        const shell = electron.shell;
        return {
            __obj__: shell,
            showItemInFolder(path) {
                shell.showItemInFolder(path);
            },
        };
    })(electron);

    this.remote = ((electron) => {
        const remote = electron.remote;
        return {
            __obj__: remote,
            getUserDataDir() {
                return remote.app.getPath('userData');
            },
        };
    })(electron);
};

module.exports.defaultFilters = {
    asar: { name: 'asar 文件', extensions: ['asar'] },
    img: { name: '图像', extensions: ['jpg', 'png', 'gif'] },
    video: { name: '视频', extensions: ['mkv', 'avi', 'mp4'] },
    cft: { name: '自定义文件', extensions: ['as'] },
    all: { name: '任意文件', extensions: ['*'] },
};

module.exports.defaultProperties = {
    打开文件: 'openFile',
    打开文件夹: 'openDirectory',
    多选: 'multiSelections',
    显示隐藏文件: 'showHiddenFiles',
};

/**
 * todo : 这部分是集合项目中的，可能大部分内容已经不需要了
 * 主要是 app.asar 中使用到的相关接口
 */
module.exports.extendMethod = {
    apps: {
        get() {
            delete require.cache[`${basePath}\\application`];
            return require(`${basePath}\\application`);
        },
        set(cb, { content }) {
            fs.writeFileSync(`${basePath}\\application.json`, JSON.stringify(content));
        },
        addAsar(cb, { asarPath, otherName }) {
            otherName = otherName ? (`${otherName}.asar`) : path.basename(asarPath);
            // fs.copyFileSync(asarPath,path.join(basePath,otherName));
            cmd.exec(`copy /Y ${asarPath} ${path.join(basePath, otherName)}`, (err, stdout, stderr) => {
                if (err) {
                    cb(false);
                } else {
                    cb(true);
                }
            });
        },
        deleteApp(name) {
            try {
                fs.unlinkSync(path.join(basePath, name));
            } catch (e) {
                console.log(e);
            }
            try {
                fs.unlinkSync(path.join(basePath, `${name}.asar`));
            } catch (e) {
                console.log(e);
            }
            try {
                fs.unlinkSync(path.join(basePath, 'tmp/', name));
            } catch (e) {
                console.log(e);
            }
        },
        checkAppExist(cb, { asarPath }) {
            asarPath = path.basename(asarPath, '.asar');
            cb(fs.existsSync(path.join(basePath, asarPath)) || fs.existsSync(path.join(basePath, `${asarPath}.asar`)));
        },
        createShortCut(cb, { targetName, name, icon }) {
            const shortCutPath = path.join(basePath, 'Shortcut.exe');
            const targetPath = path.join('"%USERPROFILE%\\Desktop\\', `${targetName}.lnk"`);
            const srcPath = path.join(basePath, '../electron.exe');
            cmd.exec(`${shortCutPath} /f:${targetPath} /a:c /t:${srcPath} /P:../${name}`, (err, stdout, stderr) => {
                if (err) {
                    cb(false);
                } else {
                    cb(true);
                }
            });
        },
        startApp(name) {
            const shortCutPath = path.join(basePath, '/../electron.exe');
            cmd.exec(`"${shortCutPath}" "../${name}"`);
        },
        quit() {
            const electron = require('electron');
            const ipc = electron.ipcRenderer;
            ipc.send('quit');
        },
    },
    path,
    fs,
    require(path, clear) {
        /**
         * 遍历缓存来查找通过特定模块名缓存下的模块
         */
        function searchCache(moduleName, callback) {
            //  通过指定的名字resolve模块
            let mod = require.resolve(moduleName);

            // 检查该模块在缓存中是否被resolved并且被发现
            if (mod && ((mod = require.cache[mod]) !== undefined)) {
                // 递归的检查结果
                (function traverse(mod) {
                    // 检查该模块的子模块并遍历它们
                    mod.children.forEach((child) => {
                        traverse(child);
                    });

                    // 调用指定的callback方法，并将缓存的module当做参数传入
                    callback(mod);
                }(mod));
            }
        }
        /**
         * 从缓存中移除module
         */
        function purgeCache(moduleName) {
            // 遍历缓存来找到通过指定模块名载入的文件
            searchCache(moduleName, (mod) => {
                delete require.cache[mod.id];
            });

            // 删除模块缓存的路径
            // 多谢@bentael指出这点
            Object.keys(module.constructor._pathCache).forEach((cacheKey) => {
                if (cacheKey.indexOf(moduleName) > 0) {
                    delete module.constructor._pathCache[cacheKey];
                }
            });
        }
        if (clear) {
            purgeCache(path);
        }
        return require(path);
    },
    paths: {
        basePath,
        projectRoot: basePath,
    },
};
